package edu.upol.paradise.compiler.types;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.apache.log4j.Logger;

public class Cons implements ParadisePair {

	static final Logger log = Logger.getLogger(Cons.class);

	private ParadiseObject car = Nil.NIL;
	private ParadiseObject cdr = Nil.NIL;
	
	public boolean isTrue() {
		return true;
	}

	public ParadiseObject car() {
		return car;
	}

	public ParadiseObject cdr() {
		return cdr;
	}
	
	public Cons setCar(ParadiseObject o){
		car = o;
		return this;
	}
	
	public Cons setCdr(ParadiseObject o){
		cdr = o;
		return this;
	}
	
	private ThreadLocal<Set<ParadisePair>> chainController = new ThreadLocal<Set<ParadisePair>>(){

		@Override
		protected Set<ParadisePair> initialValue() {
			return new HashSet<ParadisePair>();
		}
		
	};

	public boolean isList() {
		if (chainController.get().contains(this))
			return false;
		chainController.get().add(this);
		try {
			if (cdr == Nil.NIL)
				return true;
			if (!(cdr instanceof ParadisePair))
				return false;
			return ((ParadisePair)cdr).isList();
		} finally {
			chainController.get().remove(this);
		}
	}
	
	public String toString(){
		if (chainController.get().contains(this))
			return "...";
		
		chainController.get().add(this);
		
		try {
			String value = "(";
			value += car.toString();
			if (!(cdr instanceof ParadisePair)){
				value += " . " + cdr.toString() + ")";
			} else {
				if (cdr == Nil.NIL)
					value += ")";
				else {
					String str = cdr.toString();
					value += " " + str.substring(1);
				}
			}
			return value;
		} finally {
			chainController.get().remove(this);
		}
	}

	public boolean isAtom() {
		return false;
	}

	public boolean valueEquals(ParadiseObject other) {
		if (other == this) return true;
		return false;
	}
	
	public int atomHashId() {
		return 0;
	}

	public static ParadiseObject cddr(ParadisePair pair) {
		return ((ParadisePair) pair.cdr()).cdr();
	}
	
	public static class PPairIterator implements Iterator<ParadiseObject>{
		static final Logger log = Logger.getLogger(PPairIterator.class);

		private ParadisePair currentHead;
		private ParadisePair oldHead;
		public PPairIterator(ParadiseObject data) {
			currentHead = (ParadisePair) data;
		}

		public boolean hasNext() {
			return currentHead != Nil.NIL;
		}

		public ParadiseObject next() {
			ParadisePair pp = currentHead;
			oldHead = currentHead;
			currentHead = (ParadisePair) currentHead.cdr();
			return pp.car();
		}

		public void remove() {
			
		}
		
		public void set(ParadiseObject setee){
			((Cons) oldHead).setCar(setee);
		}
	}
	
	
	public static PPairIterator makePPairIterator(ParadiseObject data) {
		return new PPairIterator(data);
	}
}
